package com.tender.config;

import com.tender.entity.OrderInfoEntity;
import com.tender.enums.OrderStatusEnum;
import com.tender.events.OrderEvent;
import com.tender.service.OrderInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.persist.DefaultStateMachinePersister;
import org.springframework.statemachine.support.DefaultStateMachineContext;


import javax.annotation.Resource;
import java.util.EnumSet;

@Configuration
@EnableStateMachine(name = "orderStateMachine")
@Slf4j
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStatusEnum, OrderEvent> {

    @Resource
    private OrderInfoService orderInfoService;

    /**
     * 配置状态
     *
     * @param states
     * @throws Exception
     */
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderEvent> states) throws Exception {
        states.withStates()
                .initial(OrderStatusEnum.APPROVE_PENDING) // 设置初始状态为[init]
                .states(EnumSet.allOf(OrderStatusEnum.class));
    }

    /**
     * 配置状态转换事件关系
     *
     * @param transitions
     * @throws Exception
     */
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderEvent> transitions) throws Exception {
        transitions
                //当执行 【开始审核】操作时，将订单状态由待审核 -> 审核中
                .withExternal()
                    .source(OrderStatusEnum.APPROVE_PENDING)
                    .target(OrderStatusEnum.APPROVE_ING)
                    .event(OrderEvent.APPROVE_START)
                .and()
                //当执行 【审核失败】操作时，将订单状态由审核中 -> 审核失败
                .withExternal()
                    .source(OrderStatusEnum.APPROVE_ING)
                    .target(OrderStatusEnum.APPROVE_FAILED)
                    .event(OrderEvent.APPROVE_FAILED)
                .and()
                //当执行 【审核成功】操作时，将订单状态由审核中 -> 审核成功
                .withExternal()
                    .source(OrderStatusEnum.APPROVE_ING)
                    .target(OrderStatusEnum.APPROVE_SUCCESS)
                    .event(OrderEvent.APPROVE_SUCCESS);
    }



    /**
     * 持久化配置
     *
     * @return
     */
    @Bean
    public DefaultStateMachinePersister<OrderStatusEnum, OrderEvent, OrderInfoEntity> persister() {
        return new DefaultStateMachinePersister<>(new StateMachinePersist<OrderStatusEnum, OrderEvent, OrderInfoEntity>() {

            @Override
            public void write(StateMachineContext<OrderStatusEnum, OrderEvent> context, OrderInfoEntity order) throws Exception {
                OrderStatusEnum orderStatus = context.getState();
                log.info("订单状态持久化,订单ID：{},目标状态:{}", order.getId(), orderStatus);
                OrderInfoEntity orderInfo = new OrderInfoEntity();
                orderInfo.setId(order.getId());
                orderInfo.setStatus(orderStatus.name());
                orderInfoService.updateById(orderInfo);
            }

            @Override
            public StateMachineContext<OrderStatusEnum, OrderEvent> read(OrderInfoEntity order) throws Exception {
                log.info("恢复订单状态机状态");
                Long id = order.getId();
                OrderInfoEntity orderInfo = orderInfoService.getById(id);
                if (null == orderInfo) {
                    return new DefaultStateMachineContext<>(OrderStatusEnum.APPROVE_PENDING, null, null, null);
                }
                return new DefaultStateMachineContext<>(OrderStatusEnum.parseOrderStatusEnum(order.getStatus()), null, null, null);
            }

        });
    }

}
